package org.datascooter.impl;

import java.util.ArrayList;
import java.util.List;

import org.datascooter.utils.LangUtils;
import org.datascooter.utils.policy.Composer;
import org.datascooter.utils.policy.Constraint;

/**
 * Purposed for obtaining different sets of objects from one table depends on complicated set of values of attributes
 * 
 * @author nemo
 * 
 */
public class Condition extends Data {

	private static final long serialVersionUID = 6613758915424151951L;
	Composer operation = Composer.AND;
	public List<Condition> childList = new ArrayList<Condition>();
	public List<LogicValue> valueList = new ArrayList<LogicValue>();
	public String snipId;
	String parentId;
	Condition parent;
	DataSnip snip;
	private int hashCode = -1;

	public Condition() {
	}

	public Condition(String operation, List<Condition> childLogicList, List<LogicValue> valueListIn) {
		this(LangUtils.isEmptyString(operation) ? Composer.AND : Composer.valueOf(operation), childLogicList, valueListIn);
	}

	public Condition(Composer composer, List<Condition> childLogicList, List<LogicValue> valueListIn) {
		operation = composer;
		for (Condition condition : childLogicList) {
			addChild(condition);
		}
		for (LogicValue value : valueListIn) {
			addValue(value);
		}
	}

	public Condition(LogicValue value) {
		this.operation = Composer.AND;
		valueList.add(value);
	}

	public Condition(String name, Constraint constraint, Object value) {
		addValue(new LogicValue(name, constraint, value));
		this.operation = Composer.AND;
	}

	public Condition(String name, Object value) {
		addValue(new LogicValue(name, Constraint.EQUAL, value));
		this.operation = Composer.AND;
	}

	public Condition(Composer operation) {
		this.operation = operation;
	}

	public Condition and() {
		Condition condition = new Condition(Composer.AND);
		addChild(condition);
		return condition;
	}

	public Condition or() {
		Condition condition = new Condition(Composer.OR);
		addChild(condition);
		return condition;
	}

	public Condition and(String attribute, Constraint constraint, Object value) {
		Condition condition = null;
		if (operation.equals(Composer.AND)) {
			condition = this;
		} else {
			condition = new Condition(Composer.AND);
			addChild(condition);
		}
		condition.addValue(new LogicValue(attribute, constraint, value));
		return condition;
	}

	public Condition or(String attribute, Constraint constraint, Object value) {
		Condition condition = null;
		if (operation.equals(Composer.OR)) {
			condition = this;
		} else {
			condition = new Condition(Composer.OR);
			addChild(condition);
		}
		condition.addValue(new LogicValue(attribute, constraint, value));
		return condition;
	}

	public Condition or(String attribute, Object value) {
		return or(attribute, Constraint.EQUAL, value);
	}

	public Condition equal(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.EQUAL, value));
		return this;
	}

	public Condition notEqual(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.NOT_EQUAL, value));
		return this;
	}

	public Condition like(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.LIKE, value));
		return this;
	}

	public Condition notLike(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.NOT_LIKE, value));
		return this;
	}

	public Condition greater(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.GREATER_THAN, value));
		return this;
	}

	public Condition less(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.LESS_THAN, value));
		return this;
	}

	public Condition greaterOrEqual(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.GREATER_THAN_OR_EQUAL, value));
		return this;
	}

	public Condition lessOrEqual(String attribute, Object value) {
		addValue(new LogicValue(attribute, Constraint.LESS_THAN_OR_EQUAL, value));
		return this;
	}

	public Condition up() {
		return parent;
	}

	public DataSnip snip() {
		return snip;
	}

	public void addValue(LogicValue value) {
		valueList.add(value);
		value.conditionId = getId();
	}

	public void addChild(Condition condition) {
		childList.add(condition);
		condition.parentId = getId();
		condition.parent = this;
		condition.snip = this.snip;
	}

	@Override
	public int hashCode() {
		if (hashCode == -1) {
			hashCode = buildHash();
		}
		return hashCode;
	}

	public int buildHash() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((childList == null) ? 0 : childList.hashCode());
		result = prime * result + ((operation == null) ? 0 : operation.hashCode());
		result = prime * result + ((valueList == null) ? 0 : valueList.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (getClass() != obj.getClass())
			return false;
		Condition other = (Condition) obj;
		if (childList == null) {
			if (other.childList != null)
				return false;
		} else if (!childList.equals(other.childList))
			return false;
		if (operation == null) {
			if (other.operation != null)
				return false;
		} else if (!operation.equals(other.operation))
			return false;
		if (valueList == null) {
			if (other.valueList != null)
				return false;
		} else if (!valueList.equals(other.valueList))
			return false;
		return true;
	}

	// public Condition copy() {
	// return new Condition(operation, childList, valueList);
	// }
	@Override
	public void rebuildId() {
		super.rebuildId();
		for (Condition condition : childList) {
			condition.rebuildId();
			condition.parentId = getId();
		}
		for (LogicValue logicValue : valueList) {
			logicValue.rebuildId();
			logicValue.conditionId = getId();
		}
	}

	@Override
	public String toString() {
		return "  " + valueList;
	}
}
